package main

import (
	"bytes"
	"encoding/csv"
	"io"
	"io/ioutil"
	"log"
	"strconv"
	"strings"
	"sync"
)

var (
	authCacheIns  AuthCache
	authCacheOnce sync.Once
)

type AuthCache interface {
	Refresh()
	Get(secret string) []Authorization
	Set(secret string, auth []Authorization)
}

type authCache struct {
	lock sync.RWMutex
	data map[string][]Authorization
}

func (a *authCache) Refresh() {
	a.lock.Lock()
	defer a.lock.Unlock()
	a.data = make(map[string][]Authorization)
	for secret, auth := range *GetAuthFromCsv() {
		a.data[secret] = auth
	}
}

func (a *authCache) Get(secret string) []Authorization {
	a.lock.RLock()
	defer a.lock.RUnlock()
	return a.data[secret]
}

func (a *authCache) Set(secret string, auth []Authorization) {
	a.lock.Lock()
	defer a.lock.Unlock()
	a.data[secret] = auth
}

func GetAuthCache() AuthCache {
	authCacheOnce.Do(func() {
		authCacheIns = &authCache{
			data: make(map[string][]Authorization),
		}
	})
	return authCacheIns
}

func GetAuthFromCsv() *map[string][]Authorization {
	data, err := ioutil.ReadFile(ServerConf.AuthFilePath)
	if err != nil {
		log.Printf("csv file load err %s\n", err.Error())
		return nil
	}
	csvReader := csv.NewReader(bytes.NewReader(data))
	authResp := make(map[string][]Authorization)
	records, err := csvReader.ReadAll()
	if err == io.EOF {
		log.Printf(" read csv filed ------ ")
		return nil
	}
	for index, record := range records {
		if index > 0 {
			if len(record) != 7 {
				log.Printf("csv file parse err len %d", len(record))
				continue
			}
			secret := record[0]
			authResp[secret] = make([]Authorization, 0)
			tls, err := strconv.ParseBool(record[2])
			if err != nil {
				tls = false
			}
			authResp[secret] = append(authResp[secret], Authorization{
				IP:          strings.TrimSpace(record[1]),
				TlsRequired: tls,
				Topic:       strings.TrimSpace(record[3]),
				Channels:    strings.Split(strings.TrimSpace(record[4]), "/"),
				Subscribe:   strings.TrimSpace(record[5]),
				Publish:     strings.TrimSpace(record[6]),
			})
		}
	}
	return &authResp
}
